//
//  SWStillCameraView.swift
//  ChildStory
//
//  Created by linhan on 2016/11/10.
//  Copyright © 2016年 Aiya. All rights reserved.
//

import Foundation
import UIKit
import AVFoundation

enum UIPanGestureDirection:Int
{
    case Unknow
    case Up
    case Down
    case Left
    case Right
}

protocol SWStillCameraViewDelegate:NSObjectProtocol
{
    func stillCameraAuthorizationDenied(_ cameraView:SWStillCameraView)
    func stillCameraLaunchComplete(_ cameraView:SWStillCameraView)
    func stillCameraWillBeginExchanging(_ cameraView:SWStillCameraView) //开始前后切换
    func stillCameraDidEndExchanging(_ cameraView:SWStillCameraView)
    func stillCameraWillBeginCapturing(_ cameraView:SWStillCameraView)  //开始截图
    func stillCameraDidEndCapturing(_ cameraView:SWStillCameraView, image:UIImage?, error:Error?)
}

class SWStillCameraView: UIView
{
    weak var delegate:SWStillCameraViewDelegate?
    
    private var _adjustingFocusContext = 0
    
    //辅助线
    private var _auxiliaryLineView:UIView = UIView()
    private var _frontCamera:AVCaptureDevice?
    private var _backCamera:AVCaptureDevice?
    private var _snapshotView:UIView?
    private var _loadingView:UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
    
    private var _customFocusView:CustomFocusView = CustomFocusView()
    private var _autoFocusView:UIView = UIView()
    
    //自定义曝光
    private var _customMode:Bool = false{
        didSet{
            _customFocusView.isHidden = !_customMode
            _autoFocusView.isHidden = _customMode
            if _customMode
            {
                _customFocusView.progress = 0.5
            }
        }
    }
    private var _customExposure:Bool = false{
        didSet{
            _customFocusView.trackHidden = !_customExposure
        }
    }
    
    private var _exposureDuration:Double = 0
    private var _exposureValue:Float = 0
    private var _exposureProgress:Float = 0.5
    private var _startExposureProgress:Float = 0.5
    private var _startPoint:CGPoint = CGPointZero
    private var _panDirection:UIPanGestureDirection = UIPanGestureDirection.Unknow
    private var _lockedSuccess:Bool = false
    
    
    private var _capturing:Bool = false{
        didSet{
            if _capturing {
                if let snapshot = snapshotView(afterScreenUpdates: false)
                {
                    addSubview(snapshot)
                    snapshot.addSubview(_loadingView)
                    _loadingView.center = CGPointMake(snapshot.width * 0.5, snapshot.height * 0.5)
                    _snapshotView = snapshot
                    _loadingView.startAnimating()
                }
                
            }
            else
            {
                _loadingView.stopAnimating()
                _loadingView.removeFromSuperview()
                _snapshotView?.removeFromSuperview()
            }
        }
    }
    
    override var frame: CGRect{
        get{
            return super.frame
        }
        set{
            super.frame = newValue
            _preview.frame = bounds
            _previewLayer?.frame = bounds
            auxiliaryLineViewSetup(bounds)
        }
    }
    
    var position:AVCaptureDevicePosition = .back{
        didSet{
            if position != oldValue
            {
                var device:AVCaptureDevice?
                device = position == .back ? (_backCamera ?? _frontCamera) : (_frontCamera ?? _backCamera)
                _camera = device
            }
        }
    }
    
    //摄像头是否可切换
    var exchangeEnable:Bool
    {
        return _backCamera != nil && _frontCamera != nil
    }
    
    private var _session:AVCaptureSession = AVCaptureSession()
    private var _previewLayer: AVCaptureVideoPreviewLayer?
    private let _stillImageOutput:AVCaptureStillImageOutput = AVCaptureStillImageOutput()
    private let _preview:UIView = UIView()
    
    lazy private var _deniedTipLabel:UILabel = UILabel()
    
    init() {
        super.init(frame: CGRect.zero)
        setup()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit
    {
        NotificationCenter.default.removeObserver(self)
        trace("DEINIT SWStillCameraView")
    }
    
    private func setup()
    {
        backgroundColor = UIColor.black
        
        _deniedTipLabel = UILabel.labelWithLimitWidth(300, text: "请到 设置-》隐私-》照片中设置允许使用你的相机", font: SWAppearanceManager.themeStyle.warningTextFont)
        _deniedTipLabel.textColor = UIColor.lightGray
        _deniedTipLabel.textAlignment = .center
        _deniedTipLabel.frame = CGRectMake(10, 150, _deniedTipLabel.width, _deniedTipLabel.height)
        
        
        let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(previewTapped(_:)))
        let pinchGesture:UIPinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(previewPinch(_:)))
        let panGesture:UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(previewPan(_:)))
        
        _preview.isUserInteractionEnabled = false
        _preview.addGestureRecognizer(tapGesture)
        _preview.addGestureRecognizer(pinchGesture)
        _preview.addGestureRecognizer(panGesture)
        addSubview(_preview)
        
        
        _auxiliaryLineView.isUserInteractionEnabled = false
        _auxiliaryLineView.isHidden = true
        addSubview(_auxiliaryLineView)
    }
    
    func dispose()
    {
        _session.stopRunning()
    }
    
    //授权使用摄像头
    func authAVCapture()
    {
        let status:AVAuthorizationStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)
        if status == .authorized {
            cameraSetup()
        }
        else if status == .notDetermined{
            AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: {granted in
                DispatchQueue.main.async{
                    granted ? self.cameraSetup() : self.denyToUseCamera()
                }
            })
        }
        else {
            denyToUseCamera()
        }
    }
    
    //拒绝使用摄像头
    private func denyToUseCamera()
    {
        _deniedTipLabel.isHidden = false
        _preview.isHidden = true
        addSubview(_deniedTipLabel)
        delegate?.stillCameraAuthorizationDenied(self)
    }
    
    
    private var _deviceInput:AVCaptureDeviceInput?
    private var _camera:AVCaptureDevice?
        {
        didSet{
            delegate?.stillCameraWillBeginExchanging(self)
            _session.beginConfiguration()
            if let input = _deviceInput{
                _session.removeInput(input)
            }
            if let camera = _camera,
               let input = try? AVCaptureDeviceInput(device: camera) , _session.canAddInput(input)
            {
                _deviceInput = input
                _session.addInput(input)
                
                SWOCHelper.try({
                    camera.removeObserver(self, forKeyPath: "adjustingFocus")
                })
                camera.addObserver(self, forKeyPath: "adjustingFocus", options: .new, context: &_adjustingFocusContext)
            }
            _session.commitConfiguration()
            delegate?.stillCameraDidEndExchanging(self)
        }
    }
    
    private func cameraSetup()
    {
        //获取所有的摄像头
        if let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as? [AVCaptureDevice]
        {
            for device in devices
            {
                if device.position == .back
                {
                    _backCamera = device
                }
                else if device.position == .front
                {
                    _frontCamera = device
                }
                
                //强制关闭所有闪光灯
                setFlashMode(.off, device:device)
            }
        }
        
        guard let _ = _backCamera ?? _frontCamera else
        {
            trace("没有摄像头")
            
            return
        }
        
        _preview.isUserInteractionEnabled = true
        
        //PresetPhoto (2448.0, 3264.0)
        _session.sessionPreset = AVCaptureSessionPresetPhoto
        _previewLayer = AVCaptureVideoPreviewLayer(session: _session)
        _previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
        _previewLayer?.frame = bounds
        _preview.layer.addSublayer(_previewLayer!)
        
        _customFocusView.isHidden = true
        _preview.addSubview(_customFocusView)
        
        _autoFocusView.layer.borderWidth = 1
        _autoFocusView.layer.borderColor = UIColor.yellow.cgColor
        _autoFocusView.alpha = 0
        _autoFocusView.isUserInteractionEnabled = false
        _preview.addSubview(_autoFocusView)
        
        
        _camera = _backCamera ?? _frontCamera
        
        if _session.canAddOutput(_stillImageOutput)
        {
            _stillImageOutput.outputSettings = [AVVideoCodecKey:AVVideoCodecJPEG]
            _session.addOutput(_stillImageOutput)
        }
        
        _session.startRunning()
        
        delegate?.stillCameraLaunchComplete(self)
    }
    
    //获取静止图片
    func capture()
    {
        guard let connection = _stillImageOutput.connection(withMediaType: AVMediaTypeVideo) else{
            delegate?.stillCameraDidEndCapturing(self, image: nil, error: nil)
            return
        }
        
        //跟随设备方向
        if let orientation = AVCaptureVideoOrientation(rawValue: UIDevice.current.orientation.rawValue) , connection.isVideoOrientationSupported
        {
            connection.videoOrientation = orientation
        }
        
        // 开启视频防抖模式
        if #available(iOS 8.0, *) {
            if let support = _camera?.activeFormat.isVideoStabilizationModeSupported(AVCaptureVideoStabilizationMode.cinematic) , support
            {
                connection.preferredVideoStabilizationMode = .cinematic
            }
        }
        
        _capturing = true
        delegate?.stillCameraWillBeginCapturing(self)
        
        _stillImageOutput.captureStillImageAsynchronously(from: connection, completionHandler: {[weak self]buffer, error in
            
            DispatchQueue.main.async {
                
                if let strongself = self{
                    
                    strongself._capturing = false
                    if let buffer = buffer,
                        let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer),
                        let image = UIImage(data: imageData)
                    {
                        strongself.delegate?.stillCameraDidEndCapturing(strongself, image: image, error: nil)
                    }
                    else
                    {
                        strongself.delegate?.stillCameraDidEndCapturing(strongself, image: nil, error: error)
                    }
                }
                
            }//end main queue
            
        })//end FromConnection
        
    }
    
    private func auxiliaryLineViewSetup(_ rect:CGRect)
    {
        _auxiliaryLineView.removeAllSubview()
        
        let ContainerWidth:CGFloat = rect.width
        let ContainerHeight:CGFloat = rect.height
        let LineWidth:CGFloat =  ContainerWidth
        let LineHeight:CGFloat =  ContainerHeight
        
        
        let color:UIColor = UIColor(hex:0xd6dfeb)
        let hLine1:UIView = UIView(frame: CGRectMake(0, ContainerHeight / 3, LineWidth, 0.5))
        hLine1.backgroundColor = color
        let hLine2:UIView = UIView(frame: CGRectMake(0, ContainerHeight * 2 / 3, LineWidth, 0.5))
        hLine2.backgroundColor = color
        
        let vLine1:UIView = UIView(frame: CGRectMake(ContainerWidth / 3, 0, 0.5, LineHeight))
        vLine1.backgroundColor = color
        let vLine2:UIView = UIView(frame: CGRectMake(ContainerWidth * 2 / 3, 0, 0.5, LineHeight))
        vLine2.backgroundColor = color
        
        _auxiliaryLineView.addSubview(hLine1)
        _auxiliaryLineView.addSubview(hLine2)
        _auxiliaryLineView.addSubview(vLine1)
        _auxiliaryLineView.addSubview(vLine2)
        _auxiliaryLineView.frame = rect
    }
    
    //闪光灯模式（开、自动、关）
    private func setFlashMode(_ mode:AVCaptureFlashMode, device:AVCaptureDevice?)
    {
        if let camera = device ?? _camera , camera.hasFlash && camera.isFlashAvailable
        {
            var success:Bool = true
            do{
                try camera.lockForConfiguration()
            }
            catch _{success = false}
            
            if success
            {
                camera.flashMode = mode
                camera.unlockForConfiguration()
            }
        }
        
    }
    
    //手电筒
    private func setTorchMode(_ mode:AVCaptureTorchMode)
    {
        if let camera = _camera , camera.hasTorch && camera.isTorchAvailable
        {
            var success:Bool = true
            do{
                try camera.lockForConfiguration()
            }
            catch _{success = false}
            
            if success
            {
                camera.torchMode = mode
                camera.unlockForConfiguration()
            }
        }
    }
    
    //设定为自动曝光
    private func setContinuousAutoExposure()
    {
        if let camera = _camera
        {
            var success:Bool = true
            do{
                try _camera?.lockForConfiguration()
            }
            catch _{success = false}
            
            if success
            {
                if camera.isExposureModeSupported(AVCaptureExposureMode.autoExpose)
                {
                    camera.exposureMode = AVCaptureExposureMode.continuousAutoExposure
                }
            }
        }
    }
    
    private func showAutoFocusView(_ center:CGPoint)
    {
        _autoFocusView.alpha = 1
        _autoFocusView.frame = CGRectMake(0, 0, 80, 80)
        _autoFocusView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
        _autoFocusView.center = center
        
        UIView.animate(withDuration: 0.3, animations: {
            self._autoFocusView.transform = CGAffineTransform.identity
        }, completion: {finish in
            //延迟0.5秒后消失
            UIView.animate(withDuration: 0.3, delay: 0.5, options: .beginFromCurrentState, animations: {
                self._autoFocusView.alpha = 0
            }, completion: nil)
        })
    }
    
    //显示焦点框
    private func showCustomFocusView(_ center:CGPoint)
    {
        if _customFocusView.isHidden
        {
            _customFocusView.isHidden = false
        }
        
        _customFocusView.center = center
        _customFocusView.show({
            //延迟0.5秒后消失
            self.hideFocusViewDelay()
        })
    }
    
    private var _hideIntervalID:String = ""
    private func hideFocusViewDelay()
    {
        clearInterval(_hideIntervalID)
        _hideIntervalID = setTimeout(2, closure: {[weak self] in
            UIView.animate(withDuration: 0.2, animations: {
                self?._customFocusView.alpha = 0.5
            })
        })
    }
    
    private func exposureReset()
    {
        _customMode = false
        if _customExposure
        {
            _customExposure = false
            _exposureProgress = 0.5
            setContinuousAutoExposure()
            showAutoFocusView(_preview.bounds.center)
            trace("exposureReset")
        }
    }
    
    //开始滑动
    private func panBegan(_ direction:UIPanGestureDirection)
    {
        if direction == .Up || direction == .Down
        {
            if !_customMode
            {
                return
            }
            
            clearInterval(_hideIntervalID)
            
            _lockedSuccess = true
            if !_customExposure
            {
                _customExposure = true
                
                if #available(iOS 8.0, *) {
                    if let camera = _camera
                    {
                        _exposureProgress = 0.5
                        _startExposureProgress = 0.5
                        _exposureValue = camera.iso
                        _exposureDuration = CMTimeGetSeconds(camera.exposureDuration)
                        print("timescale:", _exposureDuration, camera.exposureDuration.timescale)
                    }
                }
            }
            
            do{
                try _camera?.lockForConfiguration()
            }
            catch _{_lockedSuccess = false}
        }
    }
    
    //滑动过程
    private func panChanged(_ direction:UIPanGestureDirection, distance:CGFloat)
    {
        if direction == .Up || direction == .Down
        {
            if let activeFormat = _camera?.activeFormat,
                let camera = _camera, _lockedSuccess && _customMode
            {
                var value:Float = _startExposureProgress + Float(-distance * 0.3 / _preview.frame.height)
                value = min(max(value, 0), 1)
                if value != _exposureProgress
                {
                    _exposureProgress = value
                    _customFocusView.progress = value
                    
                    if #available(iOS 8.0, *) {
                        if camera.isExposureModeSupported(AVCaptureExposureMode.custom)
                        {
                            let IOSLength = (activeFormat.maxISO - activeFormat.minISO) * 0.5
                            let maxDuration = CMTimeGetSeconds(activeFormat.maxExposureDuration)
                            let minDuration = CMTimeGetSeconds(activeFormat.minExposureDuration)
                            let durationLength = (maxDuration - minDuration) * 0.5
                            //
                            var ISO = _exposureValue + (value - 0.5) * IOSLength
                            ISO = min(max(ISO, activeFormat.minISO), activeFormat.maxISO)
                            var duration = _exposureDuration + Double(value - 0.5) * durationLength
                            duration = min(max(duration, minDuration), maxDuration)
                            let time = CMTimeMakeWithSeconds(duration, camera.exposureDuration.timescale)
                            //print("ISO", ISO, activeFormat.minISO, activeFormat.maxISO)
                            //print("duration", duration, minDuration, maxDuration)
                            
                            //camera.exposureMode = AVCaptureExposureMode.Custom
                            camera.setExposureModeCustomWithDuration(time, iso: ISO, completionHandler: {time in
                                //print("setExposure completionHandler:", time)
                                
                            })//end setExposureMode
                        }//if isExposureModeSupported
                    }//end #available iOS 8
                }//end value != _exposureProgress
            }//end
        }//end direction
    }
    
    //滑动结束
    private func panEnded(_ direction:UIPanGestureDirection)
    {
        if direction == .Up || direction == .Down
        {
            if _customMode && _lockedSuccess
            {
                _camera?.unlockForConfiguration()
            }
            hideFocusViewDelay()
        }
    }
    
    //MARK ============================================================================================
    //==============================        KVO       ===============================
    //=================================================================================================
    
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
    {
        if keyPath == "adjustingFocus"
        {
            if let adjusting = change?[NSKeyValueChangeKey(rawValue: "new")] as? Int, adjusting == 0
            {
                var exposureTargetOffset:Float = 0
                if #available(iOS 8.0, *) {
                    exposureTargetOffset = _camera?.exposureTargetOffset ?? 0
                }
                //重新对焦后，如果曝光过渡或者曝光不足，则转为自动曝光
                let OffsetThreshold:Float = 1.5
                if abs(exposureTargetOffset) > OffsetThreshold
                {
                    exposureReset()
                }
            }
        }
    }
    
    //MARK ============================================================================================
    //==============================        Selector       ===============================
    //=================================================================================================
    
    //点击对焦
    @objc private func previewTapped(_ gesture:UITapGestureRecognizer)
    {
        if let camera = _camera
        {
            let tapPoint = gesture.location(in:self)
            showCustomFocusView(tapPoint)
            if let point = _previewLayer?.captureDevicePointOfInterest(for: tapPoint)
            {
                var success:Bool = true
                do{
                    try _camera?.lockForConfiguration()
                }
                catch _{success = false}
                
                if success
                {
                    //对点击点 自动对焦
                    if camera.isFocusPointOfInterestSupported && camera.isFocusModeSupported(AVCaptureFocusMode.autoFocus)
                    {
                        _camera?.focusPointOfInterest = point
                        _camera?.focusMode = AVCaptureFocusMode.continuousAutoFocus
                    }
                    
                    //对点击点 曝光
                    if camera.isExposurePointOfInterestSupported && camera.isExposureModeSupported(AVCaptureExposureMode.autoExpose)
                    {
                        camera.exposureMode = AVCaptureExposureMode.continuousAutoExposure
                    }
                    camera.exposurePointOfInterest = point
                    
                    //每次点击时，自动曝光
                    _customExposure = false
                    _customMode = true
                    _camera?.unlockForConfiguration()
                }
                
            }
            
            
        }
        
    }
    
    //拖动调整曝光
    @objc private func previewPan(_ recognizer:UIPinchGestureRecognizer)
    {
        if recognizer.state == .began
        {
            _startPoint = recognizer.location(in: _preview)
            _panDirection = .Unknow
            _startExposureProgress = _exposureProgress
        }
        else if recognizer.state == .changed
        {
            let point:CGPoint = recognizer.location(in: _preview)
            let xDistance:CGFloat = point.x - _startPoint.x
            let yDistance:CGFloat = point.y - _startPoint.y
            let distance:CGFloat = sqrt(pow(abs(xDistance), 2) + pow(abs(yDistance), 2))
            let radian:CGFloat = atan(yDistance / xDistance)
            if distance > 15
            {
                if _panDirection == .Unknow
                {
                    //左右滑动
                    if abs(radian) < 0.2
                    {
                        _panDirection = xDistance > 0 ? .Right : .Left
                        panBegan(_panDirection)
                    }
                        //上下滑动
                    else if abs(radian) > 1.3
                    {
                        _panDirection = yDistance > 0 ? .Down : .Up
                        panBegan(_panDirection)
                    }
                }
            }
            
            if _panDirection != .Unknow
            {
                if _panDirection == .Up || _panDirection == .Down
                {
                    panChanged(_panDirection, distance:yDistance)
                }
            }
        }
        else if recognizer.state == .ended || recognizer.state == .cancelled
        {
            panEnded(_panDirection)
        }
    }
    
    //缩放
    @objc private func previewPinch(_ gesture:UIPinchGestureRecognizer)
    {
        if gesture.state != .changed {
            return
        }
        
        let pinchScale:CGFloat = gesture.scale - 1
        let scale:CGFloat = (_camera?.videoZoomFactor ?? 1) + pinchScale
        if scale >= 1 && scale <= 3
        {
            var success:Bool = true
            do{
                try _camera?.lockForConfiguration()
            }
            catch _{success = false}
            
            if success
            {
                //_camera?.cancelVideoZoomRamp()
                _camera?.ramp(toVideoZoomFactor: scale, withRate: 20)
                _camera?.unlockForConfiguration()
            }
        }
    }
    
    
    
}


fileprivate class CustomFocusView: UIView
{
    
    private let Size:CGSize = CGSizeMake(80, 80)
    private let SliderWidth:CGFloat = 20
    private let SliderHeight:CGFloat = 100
    
    private var _thumb:UIView = UIView()
    private var _track:UIView = UIView()
    private var _slider:UIView = UIView()
    private var _rectView:UIView = UIView()
    
    init()
    {
        super.init(frame: CGRectZero)
        setup()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit
    {
        print("DEINIT CustomFocusView")
    }
    
    var progress:Float = 0.5 {
        didSet{
            let value = min(max(1 - progress, 0),1)
            let diameter = _thumb.width
            _thumb.y = (SliderHeight - diameter) * CGFloat(value)
        }
    }
    
    var trackHidden:Bool = true{
        didSet{
            _track.isHidden = trackHidden
        }
    }
    
    private func setup()
    {
        isUserInteractionEnabled = false
        
        _rectView.layer.borderWidth = 1
        _rectView.layer.borderColor = UIColor.yellow.cgColor
        addSubview(_rectView)
        
        
        _slider.frame = CGRectMake(0, 0, SliderWidth, SliderHeight)
        addSubview(_slider)
        
        _track.frame = CGRectMake(SliderWidth * 0.5, 0, 1, SliderHeight)
        _track.backgroundColor = UIColor.yellow
        _slider.addSubview(_track)
        
        _thumb.frame = CGRectMake(0, 0, SliderWidth, SliderWidth)
        _thumb.backgroundColor = UIColor.yellow
        _thumb.layer.cornerRadius = SliderWidth * 0.5
        _thumb.layer.masksToBounds = true
        _slider.addSubview(_thumb)
        
        progress = 0.5
        trackHidden = true
    }
    
    func show(_ completion:@escaping ()->Void)
    {
        let size = CGSizeMake(Size.width * 1.3, Size.width * 1.3)
        _rectView.frame = CGRectMake(-size.width * 0.5, -size.height * 0.5, size.width, size.height)
        _slider.center = CGPointMake(_rectView.right + self._slider.width, 0)
        UIView.animate(withDuration: 0.3, animations: {
            let newSize = self.Size
            self._rectView.frame = CGRectMake(-newSize.width * 0.5, -newSize.height * 0.5, newSize.width, newSize.height)
            self._slider.center = CGPointMake(self._rectView.right + self._slider.width, 0)
        }, completion: {finish in
            completion()
        })
    }
    
}
